本发明公开了一种面向GPU的双调排序方法和系统,通过一个线程操作向量来模拟多个比较器,多个线程并行执行归并排序,其中对同一个工作组内的线程同步使用同步函数来完成,对不同工作组内的线程同步通过CPU完成,进一步的可以使用多个向量来模拟更大长度向量,在排序过程中改变比较器操作数的写回地址,对内存读写进行优化。本发明在节省存储空间的基础上,有效地减少CPU和GPU之间的同步次数、减少执行指令的总量和延时、增加GPU计算单元的利用率。
1.一种面向GPU的双调归并排序方法,其特征在于包括如下步骤:(1)将共享内存中的待排序列数据拷贝到GPU设备局部内存中;(2)判断是否需要进行向量内排序,若需要则由一个线程操作向量模拟L个比较器,多个线程并行执行归并排序;(3)将排序结果由GPU设备局部内存拷贝到共享内存中;由一个线程来模拟L×M个比较器,操作2×M个向量进行比较交换操作,每个线程内向量运算指令顺序执行,对于排序2N个元素,使用M个L长度向量模拟M×L长度向量,在执行相同数量向量运算指令下将(1+2×lgN/lg2-2×lgITEMS/lg2-2×lgM×lg2)×lgM/lg2条需CPU参与的同步转换为单线程内向量运算指令顺序执行所蕴含的隐式同步或同步函数同步,其中ITEMS为GPU一个工作组内包含的线程数;在排序过程中,改变比较器操作数的写回地址,以使局部内存读操作的地址连续,同时为防止线程间数据读写冲突,设置每个线程将需要操作的数据读入寄存器再进行比较交换操作;在排序一组向量时,若该组向量的前半部分向量地址不连续,则将该前半部分向量中的后半部与该组向量的后半部分向量中的后半部交换位置后,再执行写回共享内存的操作;或将该前半部分向量中的前半部与该组向量的后半部分向量中的前半部交换位置后,再执行写回共享内存的操作。
2.如权利要求1所述的方法,其特征在于:步骤(2)中多个线程并行执行归并排序时,对于同一个工作组内的线程同步使用同步函数来完成,对于不同工作组内的线程间同步通过CPU完成。
3.如权利要求2所述的方法,其特征在于:当一个工作组内的比较器本次和下次操作数都存在于该工作组的局部内存时,使用同步函数同步工作组内线程;当一个工作组内的比较器本次和下次操作数存放在不同的工作组局部内存时,由CPU参与线程的同步。
4.一种面向GPU的双调归并排序系统,其特征在于包括如下模块:用于将共享内存中的待排序列数据拷贝到GPU设备局部内存中的模块;用于判断是否需要进行向量内排序,若需要则由一个线程操作向量模拟L个比较器,多个线程并行执行归并排序的模块;用于将排序结果由GPU设备局部内存拷贝到共享内存中的模块;由一个线程来模拟L×M个比较器,操作2×M个向量进行比较交换操作,每个线程内向量运算指令顺序执行,对于排序2N个元素,使用M个L长度向量模拟M×L长度向量,在执行相同数量向量运算指令下将(1+2×lgN/lg2-2×lgITEMS/lg2-2×lgM×lg2)×lgM/lg2条需CPU参与的同步转换为单线程内向量运算指令顺序执行所蕴含的隐式同步或同步函数同步,其中ITEMS为GPU一个工作组内包含的线程数;在排序过程中,改变比较器操作数的写回地址,以使局部内存读操作的地址连续,同时为防止线程间数据读写冲突,设置每个线程将需要操作的数据读入寄存器再进行比较交换操作;在排序一组向量时,若该组向量的前半部分向量地址不连续,则将该前半部分向量中的后半部与该组向量的后半部分向量中的后半部交换位置后,再执行写回共享内存的操作;或将该前半部分向量中的前半部与该组向量的后半部分向量中的前半部交换位置后,再执行写回共享内存的操作。
5.如权利要求4所述的系统,其特征在于:多个线程并行执行归并排序时,对于同一个工作组内的线程同步使用同步函数来完成,对于不同工作组内的线程间同步通过CPU完成。
6.如权利要求5所述的系统,其特征在于:当一个工作组内的比较器本次和下次操作数都存在于该工作组的局部内存时,使用同步函数同步工作组内线程;当一个工作组内的比较器本次和下次操作数存放在不同的工作组局部内存时,由CPU参与线程的同步。本发明涉及一种数据排序方法,特别是一种面向GPU的基于OpenCL规范的双调归并排序方法。
背景技术排序是计算机应用中最常见的操作之一,随着并行处理技术的进一步发展,并行排序已经成为一个非常重要的研究领域。通常将并行排序分为两类:一类是直接排序,能够直接实现序列的排序;另一类是归并排序,即可以将多个有序列快速合并为一个有序列。在现有技术中,大部分的排序方法都需要开辟新的内存空间来存储排序中间步骤的结果,例如常见的快速排序、基数排序和并行排序算法中的桶排序等。双调归并排序方法能够直接在待排序列的存储空间进行数据交换,有效节省了内存开销。目前AMD的OpenCL软件开发套件(SDK)中包含了OpenCL版本的双调排序方法在GPU上的实现。其双调排序程序能够充分利用GPU的流处理器,但是排序中的同步工作完全由CPU部分完成,工作组间的线程同步需要进行上下文的切换,从而影响计算效率。因此,在节省存储空间的基础上,如何有效减少CPU和GPU之间的同步次数、减少执行指令的总量和延时、增加GPU计算单元的利用率等是本发明要解决的技术问题。
发明内容本发明的目的是为了有效减少CPU和GPU之间的同步次数、减少执行指令的总量和延时、增加GPU计算单元的利用率。为了实现上述目的,本发明提供了一种面向GPU的双调归并排序方法,包括如下步骤:(1)将共享内存中的待排序列数据拷贝到GPU设备局部内存中;(2)判断是否需要进行向量内排序,若需要则由一个线程操作向量模拟L个比较器,多个线程并行执行归并排序;(3)将排序结果由GPU设备局部内存拷贝到共享内存中。本发明还提供了一种面向GPU的双调归并排序系统,包括如下模块:用于将共享内存中的待排序列数据拷贝到GPU设备局部内存中的模块;用于判断是否需要进行向量内排序,若需要则由一个线程操作向量模拟L个比较器,多个线程并行执行归并排序的模块;用于将排序结果由GPU设备局部内存拷贝到共享内存中的模块。本发明的一种优选方案为:多个线程并行执行归并排序时,对于同一个工作组内的线程同步使用同步函数来完成,对于不同工作组内的线程间同步通过CPU完成。本发明的另一优选方案为:当一个工作组内的比较器本次和下次操作数都存在于该工作组的局部内存时,使用同步函数同步工作组内线程;当一个工作组内的比较器本次和下次操作数存放在不同的工作组局部内存时,由CPU参与线程的同步。本发明的另一优选方案为:由一个线程来模拟L×M个比较器,操作2×M个向量进行比较交换操作,每个线程内向量运算指令顺序执行。本发明的另一优选方案为:在排序过程中,改变比较器操作数的写回地址,以使局部内存读操作的地址连续,同时为防止线程间数据读写冲突,设置每个线程将需要操作的数据读入寄存器再进行比较交换操作。本发明的另一优选方案为:在排序一组向量时,若该组向量的前半部分向量不连续,则将该前半部分向量中的后半部与该后半部分向量中的后半部交换位置后,再执行写回共享内存的操作。本发明的另一优选方案为:在排序一组向量时,若该组向量的前半部分向量不连续,后半部分向量连续,则将该前半部分向量中的前半部与该后半部分向量中的前半部交换位置后,再执行写回共享内存的操作。
附图说明图1为双调归并排序网络的原理图;图2为本发明的由CPU端执行的主机程序流程;图3为本发明的GPU双调归并排序方法执行过程。
具体实施方式下面通过附图和实施例,对本发明的技术方案做进一步的详细描述。本发明主要通过以下几种方式,对现有技术中GPU双调归并排序方法做出改进:一、使用向量模拟多个比较器在传统的GPU双调归并排序方法中,一条线程作为一个比较器(comparexa0andxa0conditionallyxa0interchange),待排序列长度为比较器数的2倍。可以将比较器第一次分组,通过组号即可确定所排列的数据段是按升序还是降序排列,同时可以将比较器二次分组,通过组号能够得到该比较器操作的序列元素的位置,图1是拥有4个比较器的双调归并排序网络的简单原理示意。而在实际应用的测试中发现,仅使用单数据项进行比较的传统双调排序方法在运行中算术逻辑单元(ALU)的使用率偏低,为解决该技术问题,本发明提出一种引入向量计算的双调归并排序方法,以提高算术逻辑单元的利用率。对2N个数字进行基于标量的双调归并排序,需要N个比较器,同步次数为(lgN/lg2+2)*(lgN/lg2+1)/2-1。而使用长度为L的向量,使得一个线程由模拟一个比较器变为模拟L个比较器,在进行双调归并排序时,同步次数减少为(2+lgN/lg2-2*lgL/lg2)*(lgN/lg2-2*lgL/lg2+1)/2-1。同时使用向量可以减少线程数,即减少需要执行的指令数。排序同等规模数据时,使用长度为L的向量的指令数为基于标量的指令数的1/L,从而将大大减少计算的时间,提高排序计算的效率。二、向量同步操作优化在GPU运算中线程同步是高开销的操作,不同的同步方法产生的开销差别很大。在OpenCL规范中,一个工作组(work-group)内的线程(work-item)同步可以使用同步函数完成,同步过程不需要CPU参与,不涉及上下文(context)切换,因而该同步方法开销低。而工作组间的线程同步必须通过切换上下文到CPU设备来完成,该同步方法开销高。若GPU一个工作组内可包含的线程数为ITEMS,使用长度为L的向量进行排序操作,当(N/L>lgITEMS/lg2)时,需要CPU同步的次数为:(1+lgN/lg2-2*lgL/lg2-lgITEMS/lg2)*(lgN/lg2-2*lgL/lg2-lgITEMS/lg2)/2;而当(N/L<=lgITEMS/lg2)时,需要CPU同步的次数为0,即完全使用同步函数来进行线程内同步。基于上述分析,为了将同步开销降到最低,本实施例的方案中混合使用两种同步方法,当一个工作组内的比较器本次和下次操作数都存在于该工作组的局部内存时,使用同步函数同步工作组内线程;当一个工作组内的比较器本次和下次操作数存放在不同的工作组局部内存时,CPU参与所有线程的同步。三、使用多个向量模拟更大长度向量由于在GPU运算中,向量指令允许的向量长度有限制,为使单个线程模拟超过最大向量长度数的比较器,可以使用单线程操作2组多个向量实现。使用标量的情况下,一个线程模拟一个比较器操作2个标量进行比较交换;使用L长度的向量情况下,一个线程模拟L个比较器操作2个向量进行比较交换。为使单个线程模拟超过向量指令允许的最大向量长度数的比较器,本实施例的方案中,使用单线程操作2组向量,每组向量中包含多个向量。即L长度向量情况下一个线程模拟L*M个比较器操作2*M个向量进行比较交换。单线程操作2组M个L长度向量,即相当于使用M个L长度向量模拟M*L长度向量,从而增加单个工作组内处理的数据量。对于排序2N个元素,使用M个L长度向量模拟M*L长度向量,与实施例一、二中使用L长度向量相比,在执行相同数量向量运算指令下将(1+2*lgN/lg2-2*lgITEMS/lg2-2*lgM*lg2)*lgM/lg2条需CPU参与的同步转换为单线程内向量运算指令顺序执行所蕴含的隐式同步或同步函数同步,隐式同步的开销要小于CPU参与的同步和OpenCL同步函数的同步。四、基于向量的局部内存读操作优化传统的双调归并排序方法中,一组线程进行内存读操作的地址在大多数情况下是不连续的,当局部内存读操作的内存地址不连续时将降低缓存命中率、产生额外的bank冲突。由于下次操作的数据位置在本次操作中是能够计算的,可以通过改变数据写回地址实现下次局部内存读操作的地址连续。改变后由于比较器读操作的内存地址和写操作的内存地址不一致,为了防止线程间数据读写冲突,设置每个线程将需要操作的数据读入寄存器再进行比较交换操作,在写回操作前设置同步操作确保所有数据已经被处理。通过对双调归并排序的推导,得到如下数据写回地址换算规则,对CPU和GPU构架均适用。a)设本次数据操作是将两组m个元素的单调序列归并为一个2*m个元素的单调序列的一系列操作中的一步,下次数据操作仍然属于这一系列操作,转换公式为(仅适用于包含8个元素的向量):id1=((Id>>jg_3)<<jg_3)+Id-g2l_setoff;id2=id1+(1<<jg_3);id1=(id1>>(jg_3-1))%2==0?id1-((id1>>(jg_3-1))>>1)*(1<<(jg_3-1)):id1+(((1<<(10-jg_3))-(id1>>(jg_3-1)))>>1)*(1<<(jg_3-1));id2=(id2>>(jg_3-1))%2==0?id2-((id2>>(jg_3-1))>>1)*(1<<(jg_3-1)):id2+(((1<<(10-jg_3))-(id2>>(jg_3-1)))>>1)*(1<<(jg_3-1));其中id1、id2为比较器操作的两个数的写回下标,Id为线程的全局编号,jg_3为将两个m个元素的单调序列归并为一个2*m个元素的单调序列的一系列操作中“总步数和现步骤编号的差”,g2l_setoff为“两倍的工作组内线程数乘以工作组编号”。b)设本次数据操作是将两个m个元素的单调序列归并为一个2*m个元素的单调序列的一系列操作中的一步,但下次数据操作是将两个2*m个元素的单调序列归并为一个2*2*m个元素的单调序列的一系列操作中的一步,同时下一次数据操作所隶属的一系列操作完成后不需要CPU参与同步,转换公式为(仅适用于包含8个元素的向量):id1=((Id>>jg_3)<<jg_3)+Id-g2l_setoff;id2=id1+(1<<jg_3);id1=(id1>>(ig_3+1))%2==0?id1-((id1>>(ig_3+1))>>1)*(1<<(ig_3+1)):id1+(((1<<(8-ig_3))-(id1>>(ig_3+1)))>>1)*(1<<(ig_3+1));id2=(id2>>(ig_3+1))%2==0?id2-((id2>>(ig_3+1))>>1)*(1<<(ig_3+1)):id2+(((1<<(8-ig_3))-(id2>>(ig_3+1)))>>1)*(1<<(ig_3+1));b条的规则设置是为了将不需要CPU同步的几组系列操作通过同步函数联系起来。c)类似于上述b条情况,但下一次数据操作所隶属的一系列操作完成后需CPU参与同步,转换公式为(仅适用于包含8个元素的向量):id1=((Id>>jg_3)<<jg_3)+Id-g2l_setoff;id2=id1+(1<<jg_3);d)本次数据操作需要CPU参与同步,转换公式为(仅适用于包含8个元素的向量):id1=((Id>>(jg_3-3))<<(jg_3-3))+Id;id2=id1+(1<<(jg_3-3));五、降低向量写操作bank冲突的普适方法ATIxa0GPU以64个线程一组(warp)执行,但局部内存操作要分成两次的32个线程一组对局部内存进行操作;Nvidiaxa0GPU以32个线程一组执行,局部内存操作同样以32个线程一组对局部内存进行操作。ATIxa0GPU的64个局部内存操作要分成两次的32个线程作为一组对局部内存进行操作时,32个线程将写操作32*2个向量,那么32*2个向量需要2次局部内存写操作完成,可以分两种情况:一是32*2个向量地址不连续,由于算法特点前32个地址连续,后32个地址连续,对于32个连续地址的局部内存数据操作的bank冲突已经最低;二是32*2个向量地址连续,但前32个地址不连续,此时需要交换一部分数据在寄存器中的位置将bank冲突降至最低;但在Nvidiaxa0GPU平台上由于32个线程一组,局部内存操作同样以32线程为一组,所以写局部内存数据一定bank冲突最低。只针对ATIxa0GPU特点制定针对ATIxa0GPU的局部内存写操作优化策略“当32个线程操作的64个向量的前32个地址不连续时,在比较器操作完成后将此32个线程中奇数编号线程对应的id1和id2关联的两个向量元素进行再次交换,达到每组32个写操作中奇数线程先写回大数(或小数)但偶数线程先写回相反的小数(或大数),但这32个元素在地址上是一定连续的”。此方法是以增加计算量的方式减少bank冲突。设备全局内存的bank数为同时执行写操作线程数的1/M(M=2i,i为正整数),一个向量占据K个bank通道(K=2j,j为正整数,K小于bank数)的广义情况下,此时“同时执行写操作线程数*K/bank数=2
i+j,即每次写操作需要2
i+j倍的bank通道,但2
i+j>1所以必然存在bank冲突”。普适的向量写操作bank冲突优化方法为“排序一组连续向量,前半部向量不连续时,将前半部的后半部(或前半部)向量和后半部的后半部(或前半部)向量交换位置后再执行写回设备全局内存的操作将使bank冲突降至最低”。鉴于一次写操作的向量地址必然等间隔,一组比较器执行一次比较操作的两次写操作的向量地址必然连续的特点,可保证上述方法的正确性。如附图2所示,本发明的面向GPU的双调归并排序方法中,由CPU端执行的主机程序流程为:步骤1:查找OpenCL支持的计算平台,选择相应的计算平台后创建支持特定特备的上下文(Context);步骤2:查找上下文支持的计算设备,并关联上下文和计算设备;步骤3:查询计算设备的属性,创建待排序队列并关联到上下文和具体设备;步骤4:创建Kernel程序并关联到上下文后,编译具体设备上运行的Kernel程序;步骤5:选择Kernel程序中一个能够在设备上运行的函数作为Kernel函数;步骤6:创建设备内存和主机内存间的缓冲区;步骤7:对Kernel函数的参数赋值,初次启动Kernel函数;步骤8:判断待排序队列的排序是否已完成,若完成则导出设备内存数据至主机内存;步骤9:若未完成,继续判断是否更新Kernel函数参数,若不需更新则启动Kernel函数后跳转到步骤8继续执行,若需要更新则重新对Kernel函数参数赋值并启动Kernel函数后跳转到步骤8继续执行。如附图3所示,为由Kernel函数执行的本发明的GPU双调归并排序方法执行过程:步骤1:读取Kernel函数参数,若为GPU线程需要多次局部同步(工作组内同步)截止到需要全局同步(情况3),则执行步骤3;若为GPU线程进行一次比较操作后需要全局同步(情况2),则执行步骤4后强制返回至步骤1;若为排序起始到首次GPU线程需要全局同步截止(情况1),则执行步骤2;步骤2:将设备共享内存数据拷贝到设备局部内存,进行向量内排序;步骤3:执行数据读合并操作;步骤4:向量比较交换数据,判断是否需要进行向量内排序,若是则进行向量内排序,若否则继续执行步骤5;步骤5:判断是否需要降低bank冲突操作,若是则执行半数线程交换操作,若否则继续执行步骤6;步骤6:判断是否终止排序,若是则将设备局部内存数据拷贝到设备共享内存,若否则返回至步骤1。通过本发明的技术方案,可以实现在节省存储空间的基础上,有效减少CPU和GPU之间的同步次数、减少执行指令的总量和延时、增加GPU计算单元的利用率。以上所述的具体实施方式,对本发明的目的、技术方案和有益效果进行了进一步详细说明,所应理解的是,以上所述仅为本发明的具体实施方式而已,并不用于限定本发明的保护范围,凡在本发明的精神和原则之内,所做的任何修改、等同替换、改进等,均应包含在本发明的保护范围之内。